import types

MODULE_TEMPLATE = """<?xml version="1.0" encoding="UTF-8" ?>
<Module>
    %s
    <ModulePrefs %s>
        %s
    </ModulePrefs>
    <Content %s>
        %s
    </Content>
</Module>"""

CONTENT_HTML_TEMPLATE = """<![CDATA[
        %s
        ]]>"""

VALID_DATA_TYPES = ['string', 'bool', 'enum', 'hidden', 'list', 'location']

class GadgetModule(object):
    """http://code.google.com/apis/gadgets/docs/reference.html#XML_Ref"""
    content_html = ''
    content_type = 'html' # 'url'
    content_href = None
    content_template = CONTENT_HTML_TEMPLATE

    user_prefs = [] # UserPref* objects list
    mayrequires = [] # MayRequire objects list
    requires = [] # string list with libraries names
    locales = [] # Locale objects list

    url = ''

    # ModulePrefs
    module_prefs = dict(
            title = None,           # Required
            description = None,     # Required
            author = None,          # Required
            author_email = None,    # Required
            author_location = None,
            author_affiliation = None,
            author_photo = None,
            author_aboutme = None,
            author_link = None,
            author_quote = None,
            screenshot = None,
            thumbnail = None,
            title_url = None,
            directory_title = None,
            height = None,
            scrolling = None,
            singleton = None,
            )

    def __init__(self, content='', url=''):
        self.content_html = content or self.content_html

        self.create_default_prefs()

        __user_prefs = self.user_prefs
        self.user_prefs = []
        for u in __user_prefs:
            new = u.clone()
            new.gadget = self
            self.user_prefs.append(new)

        __mayrequires = self.mayrequires
        self.mayrequires = []
        for u in __mayrequires:
            new = u.clone()
            new.gadget = self
            self.mayrequires.append(new)

        __locales = self.locales
        self.locales = []
        for u in __locales:
            new = u.clone()
            new.gadget = self
            self.locales.append(new)

    def create_default_prefs(self):
        for k in self.module_prefs:
            if hasattr(self, k):
                self.module_prefs[k] = getattr(self, k)

    def get_locale(self, lang='ALL', country='ALL'):
        ret = None

        for locale in self.locales:
            if (not ret and locale.default) or (locale.lang == lang and locale.country == country):
                ret = locale

        return ret

    def render(self):
        user_prefs_rendered = "\n".join([u.render() for u in self.user_prefs])
        module_prefs_rendered = ' '.join(['%s="%s"' % (k, self.render_module_pref_value(k,v)) for k, v in self.module_prefs.items() if v])
        module_prefs_content = "%s\n%s\n%s" %( 
                "\n".join([m.render() for m in self.mayrequires]), 
                "\n".join([Require(r).render() for r in self.requires]), 
                "\n".join([l.render() for l in self.locales]) )

        content_params = 'type="%s"' % self.content_type

        if self.content_type == 'html':
            content_rendered = self.content_template % self.content_html
        else:
            content_rendered = ''
            content_params += ' href="%s"' % self.content_href

        ret = MODULE_TEMPLATE %( user_prefs_rendered, module_prefs_rendered, module_prefs_content, content_params, content_rendered )

        return ret

    def render_module_pref_value(self, key, value):
        if value == True:
            return 'true'
        elif value == False:
            return 'frue'
        else:
            return value

    def userpref_by_name(self, name):
        ret = [u for u in self.user_prefs if u.name == name]
        return ret and ret[0] or None

    def get_userprefs_values(self, source):
        for k, v in source.items():
            try:
                self.userpref_by_name(k).default_value = v
            except Exception, e:
                raise e

class GadgetElement(object):
    gadget = None

    def __init__(self, gadget=None):
        self.gadget = gadget

class UserPref(GadgetElement):
    """http://code.google.com/apis/gadgets/docs/reference.html#Userprefs_Ref"""
    name = None
    display_name = None
    datatype = 'string' # VALID_DATA_TYPES
    default_value = None
    urlparam = None
    required = False
    enumvalues = {} # a dict
    template = "<UserPref %s>\n%s\n</UserPref>"

    def __init__(self, name, datatype=None, display_name=None, default_value=None, urlparam=None, required=None, enumvalues={}):
        self.name = name
        self.datatype = datatype or self.datatype
        self.display_name = display_name or self.name
        self.default_value = default_value is not None and default_value or self.default_value
        self.urlparam = urlparam or self.urlparam
        self.required = required is not None and required or self.required
        self.enumvalues = enumvalues or self.enumvalues

        if self.datatype not in VALID_DATA_TYPES:
            raise Exception('Invalid datatype (%s). It must be on of them: %s' %( self.datatype, ','.join(VALID_DATA_TYPES) ))

    def render(self, childs='', default_value=None):
        params = 'name="%s"' % self.name
        params += ' datatype="%s"' % self.datatype
        params += ' display_name="%s"' % self.display_name

        default_value = default_value or self.default_value
        params += default_value is not None and ' default_value="%s"' % default_value or ''

        params += self.urlparam and ' urlparam="%s"' % self.urlparam or ''
        params += self.required and ' required="true"' or ''

        return self.template %( params, childs)

    def clone(self):
        """Prototype design pattern style function to clone an instance"""
        new = self.__class__(
                self.name,
                datatype = self.datatype,
                display_name = self.display_name,
                default_value = self.default_value,
                urlparam = self.urlparam,
                required = self.required,
                enumvalues = self.enumvalues,
                )
        new.template = self.template
        return new

class UserPrefString(UserPref):
    """http://code.google.com/apis/gadgets/docs/fundamentals.html#string"""
    datatype = 'string'
    template = "<UserPref %s>%s</UserPref>"

class UserPrefBool(UserPref):
    """http://code.google.com/apis/gadgets/docs/fundamentals.html#bool"""
    datatype = 'bool'
    default_value = False
    template = "<UserPref %s>%s</UserPref>"

class UserPrefEnum(UserPref):
    """http://code.google.com/apis/gadgets/docs/fundamentals.html#enum"""
    datatype = 'enum'
    enum_tpl = '<EnumValue value="%s" display_value="%s"/>'

    def __init__(self, name, enumvalues={}, **kwargs):
        super(UserPrefEnum, self).__init__(name, **kwargs)
        self.enumvalues = enumvalues

    def render(self):
        enumvalues = "\n".join([self.enum_tpl %( k, v ) for k, v in self.enumvalues.items()])
        
        return super(UserPrefEnum, self).render(childs=enumvalues)

    def clone(self):
        """Prototype design pattern style function to clone an instance"""
        new = super(UserPrefEnum, self).clone()
        new.enum_tpl = self.enum_tpl
        return new

class UserPrefHidden(UserPref):
    """http://code.google.com/apis/gadgets/docs/fundamentals.html#hidden"""
    datatype = 'hidden'
    template = "<UserPref %s>%s</UserPref>"

class UserPrefList(UserPref):
    """http://code.google.com/apis/gadgets/docs/fundamentals.html#list"""
    datatype = 'list'
    template = "<UserPref %s>%s</UserPref>"

    def render(self):
        default_value = self.default_value and '|'.join(self.default_value) or None
        
        return super(UserPrefList, self).render(default_value=default_value)

class UserPrefLocation(UserPref):
    """http://code.google.com/apis/gadgets/docs/fundamentals.html#location"""
    datatype = 'location'
    template = "<UserPref %s>%s</UserPref>"

class MayRequire(GadgetElement):
    """http://code.google.com/apis/gadgets/docs/reference.html#MayRequire_Ref"""
    type = None
    value = None
    min_version = None
    cdata = ''
    template = '<MayRequire type="%s" value="%s" %s>%s</MayRequire>'

    def __init__(self, type, value, min_version=None, cdata=''):
        self.type = type
        self.value = value
        self.min_version = min_version
        self.cdata = cdata

    def render(self):
        params = self.min_version and 'min_version="%s"' % self.min_version or ''

        return self.template %( self.type, self.value, params, self.cdata )

    def clone(self):
        """Prototype design pattern style function to clone an instance"""
        new = self.__class__(
                self.type,
                self.value,
                min_version = self.min_version,
                cdata = self.cdata
                )
        new.template = self.template
        return new

class Require(GadgetElement):
    """http://code.google.com/apis/gadgets/docs/reference.html#Feature_Libs"""
    feature = None
    template = '<Require feature="%s"/>'

    def __init__(self, feature=None):
        self.feature = feature or self.feature

    def render(self):
        return self.template % self.feature

class Locale(GadgetElement):
    """
    References:
     - http://code.google.com/apis/gadgets/docs/reference.html#L10N_Ref
     - http://code.google.com/apis/gadgets/docs/i18n.html
    """
    lang = None
    country = None
    messages = None
    message_bundle = None
    default = False
    language_direction = None # 'ltr' or 'rtl'
    template = '<Locale %s/>'

    def __init__(self, lang='ALL', country='ALL', messages=None, message_bundle=None, default=False, language_direction=None):
        self.lang = lang
        self.country = country
        self.messages = messages
        self.message_bundle = message_bundle
        self.default = default
        self.language_direction = language_direction

    def render(self):
        params = ''
        params += self.lang != 'ALL' and ' lang="%s"' % self.lang or ''
        params += self.country != 'ALL' and ' country="%s"' % self.country or ''
        params += self.language_direction and ' language_direction="%s"' % self.language_direction or ''

        messages_url = type(self.messages) == types.StringType and self.messages or self.render_message_bundle_url()
        params += ' messages="%s"' % messages_url

        return self.template % params

    def render_message_bundle_url(self):
        lang = self.lang and self.lang or 'ALL'
        country = self.country and self.country or 'ALL'

        return "%smessages/%s_%s.xml" %( self.gadget.url, lang, country )

    def clone(self):
        """Prototype design pattern style function to clone an instance"""
        new = self.__class__(
                lang = self.lang,
                country = self.country,
                messages = self.messages,
                message_bundle = self.message_bundle,
                default = self.default,
                language_direction = self.language_direction
                )
        new.template = self.template
        return new

class MessageBundle(GadgetElement):
    messages = {}
    template = "<messagebundle>\n%s\n</messagebundle>"
    template_msg = '<msg name="%s">%s</msg>'

    def __init__(self, messages={}):
        self.messages = messages

    def render(self):
        sorted_keys = self.messages.keys()
        sorted_keys.sort()
        items = "\n".join([self.template_msg %(k, self.messages[k]) for k in sorted_keys])
        return self.template % items

